﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using MindFactorial.LaunchPoint.Common;
using System.Xml;
using System.Security.Principal;
using System.Security.Policy;
using System.Security;
using System.Security.Permissions;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using MindFactorial.LaunchPoint.Runtime;
using System.IO;
using System.ComponentModel;
using System.Windows.Forms;
using System.Reflection;
using System.Drawing.Printing;

namespace MindFactorial.LaunchPoint.Runtime
{
    /// <summary>
    /// Represents the domain that runnable components run in,
    /// security may be added in the launching process to restrict
    /// the available resources for this domain
    /// </summary>
    internal class RunnableComponentDomain : ImmortalIPCObject
    {

        bool _run = true;
        string _app_path = null, _runnabletypename = null, _appdomain_name = null;
        Xml _configxml = null;
        ConnectionProviderCollection _providerCollection = null;
        AppDomain _dom;
        LaunchPointApplication runnable = null;
        LaunchPointConfiguration _configuration;

        /// <summary>
        /// Gets a reference to the runnable component domain started by this application domain
        /// </summary>
        public RunnableComponentDomain IsolatedDomain
        {
            get
            {
                return this;
            }
        }

        public bool Running
        {
            get
            {
                return _run;
            }
        }

        internal void Stop()
        {
            _run = false;
        }

        internal void RestartLastApp()
        {

            string nameAdjustment = string.Format("_restart_[{0}]", Guid.NewGuid().ToString());
            Run(_configuration, _appdomain_name + nameAdjustment);
        }

        /// <summary>
        /// Cleanup resources associated with the runtime 
        /// </summary>
        internal void CleanupRuntimeResources()
        {

            foreach (RuntimeApplicationInstanceWrapper wrapper in RuntimeInstanceRegistry.ApplicationRegistry.Values)
            {
                wrapper.Runnable.Shutdown();       //send shutdown Message to application
                //AppDomain.Unload(wrapper.ApplicationDomain);    //unload application domain
            }
            //explicitly exit after runnable is cleaned up
            //System.Windows.Forms.Application.Exit();
        }

        


        internal void Run(LaunchPointConfiguration configuration, string appdomain_name)
        {
            //set state
            this._app_path = configuration.Application.Path;
            string full_path = Path.GetFullPath(this._app_path);
            this._runnabletypename = configuration.Application.RunnableTypeName;
            this._appdomain_name = appdomain_name;
            this._configuration = configuration;



            try
            {

                //setup security
                Evidence ev = new Evidence();
                NamedPermissionSet nps = null;
                if (nps == null)
                {
                    nps = new NamedPermissionSet("one", PermissionState.Unrestricted);
                 //   nps.AddPermission(new UIPermission(UIPermissionWindow.AllWindows));
                    nps.AddPermission(new FileIOPermission(PermissionState.None));
                    nps.AddPermission(new FileIOPermission(FileIOPermissionAccess.AllAccess, full_path));
                    nps.AddPermission(new WebPermission(NetworkAccess.Connect, new Regex("(.+)")));
                    nps.AddPermission(new SecurityPermission(SecurityPermissionFlag.AllFlags));
                    nps.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.MemberAccess));
                    nps.AddPermission(new RegistryPermission(PermissionState.None));
                    nps.AddPermission(new PrintingPermission(PrintingPermissionLevel.AllPrinting));
                    
                }



                //setup safe assemblies
                // StrongName sn = GetStrongName(Assembly.GetExecutingAssembly());
                AppDomainSetup setup = new AppDomainSetup();
                setup.ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;



                _dom = AppDomain.CreateDomain(appdomain_name, ev, setup, nps);//, new StrongName[] { sn });
                runnable = (LaunchPointApplication)_dom.CreateInstanceFromAndUnwrap(this._app_path, this._runnabletypename);

                #region old stuff

                //#region Synchronously Connect to BRE
                ////setup event handler for bre communication
                //runnable._breconnect += delegate(Guid session_id, string channel, string adapterID, string ruleID, string arguments)
                //{

                //    Logger.Log("LaunchPoint.Runtime: Sending BRE Request {0}{1}{2}", adapterID, ruleID, arguments);

                //    //create security information to pass down
                //    WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent();
                //    string security = string.Format("<Security User=\"{0}\" />", runnable.GeCurrentRunnableUser());

                //    //find connection provider based on transport target parameter
                //    RuntimeConnectionProvider provider = _providerCollection[channel];


                //    if (provider != null)
                //        return provider.CallSecureInvoke(session_id, security, adapterID, ruleID, arguments);
                //    else
                //    {
                //        string errMessage = string.Format("There is no channel with the name {0} please check LaunchPoint Runtime Configuration file for accurate name.\nAlso make sure all files are in the appropriate location", channel);
                //        Logger.Log(errMessage);
                //        return errMessage;
                //    }
                //};
                //#endregion

                //#region Asynchronously Connect to BRE
                ////setup asynchronous event handler for bre communication
                //runnable._breconnectAsync += delegate(Guid session_id, string channel, string adapterID, string ruleID, string arguments, AsyncRuleCompletedDelegate ruleCompleted)
                //{
                //    Logger.Log("LaunchPoint.Runtime: Sending BRE Request {0}{1}{2}", adapterID, ruleID, arguments);

                //    //create security information to pass down
                //    WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent();
                //    string security = string.Format("<Security User=\"{0}\" />", runnable.GeCurrentRunnableUser());

                //    //find connection provider based on transport target parameter
                //    RuntimeConnectionProvider provider = _providerCollection[channel];


                //    if (provider != null)
                //    {

                //        //create rule completed handler to intercept call
                //        AsyncRuleCompletedDelegate acb = delegate(string result)
                //        {
                //            //any interesting processing may go here (for instance auto invoke callbacks to main thread)
                //            CrossThreadDelegate ctd = delegate()
                //            {
                //                ruleCompleted(result);
                //            };
                //            LaunchPointRuntime.RuntimeUIContext.Invoke(System.Windows.Threading.DispatcherPriority.Normal, ctd);
                //        };

                //        //call provider
                //        provider.CallSecureInvokeAsync(session_id, security, adapterID, ruleID, arguments, acb);
                //    }
                //    else
                //    {
                //        //send Message back
                //        string errMessage = string.Format("There is no channel with the name {0} please check LaunchPoint Runtime Configuration file for accurate name.\nAlso make sure all files are in the appropriate location", channel);

                //        ruleCompleted(errMessage);
                //        Logger.Log(errMessage);
                //    }
                //};
                //#endregion

                ////setup event handlers for p2p broadcasting
                //runnable._broadcast += delegate(string application, string filter, string Message)
                //{
                //    Logger.Log("LaunchPoint.Runtime: Sending Broadcast Request");
                //    //TODO: add broadcasting logic here for p2p
                //    //MAKE CALL TO SERVER
                //};

                //runnable._registerIncommingMessage += delegate(IncommingMessageFiltrationHandler filter, IncommingMessageRequestHandler request_handler)
                //{

                //}; 
                #endregion


                //register in running application list            
                RuntimeInstanceRegistry.ApplicationRegistry.Add(appdomain_name, new RuntimeApplicationInstanceWrapper(appdomain_name, _dom, runnable));


                RunRunnable(ref runnable);
            }
            catch (FileNotFoundException file_ex)
            {
                PromptManager.Alert("5D008BCF-563A-48c3-B707-24572B62E669: Unable to run application " + appdomain_name + " because " + file_ex.Message);
            }
            catch (SecurityException security_ex)
            {
                PromptManager.Alert("24334A2C-25E4-42a1-BC11-D980DF94C9B2: You do not have appropriate permissions to runt he program properly");
            }
            catch (Exception ex)
            {
                //runnable.Run(xml, "one", "two", "three");
                Logger.Log("5D008BCF-563A-48c3-B707-24572B62E669:{0} {1}", ex.Message, ex.StackTrace);
                PromptManager.Alert("5D008BCF-563A-48c3-B707-24572B62E669: Unable to run application " + appdomain_name + " because " + ex.Message);
            }
        }

        private void RunRunnable(ref LaunchPointApplication runnable)
        {
            try
            {
                if (!runnable.IsRunning)
                {
                    runnable.Run(_configuration, "one", "two", "three");
                }
                else
                {
                    System.Windows.Forms.Application.Run();
                }

                //enables key storkes from being generated in wpf since the runtime isactually a
                //winform application              
                //

                //CleanupRuntimeResources();
            }
            catch (Exception ex)
            {
                //logg error
                Logger.Log("4DACB1D7-E637-48c9-A362-7F7A869ED540:{0} {1}", ex.Message, ex.StackTrace);
                throw ex;
                //runnable.RecoveryFromUnhandledException();
                //RunRunnable(ref runnable);
            }
        }

        StrongName GetStrongName(Assembly assembly)
        {
            if (assembly == null)
                throw new ArgumentNullException("assembly");

            AssemblyName assemblyName = assembly.GetName();

            // get the public key blob
            byte[] publicKey = assemblyName.GetPublicKey();
            if (publicKey == null || publicKey.Length == 0)
                throw new InvalidOperationException(
                    String.Format("{0} is not strongly named",
                    assembly));

            StrongNamePublicKeyBlob keyBlob = new StrongNamePublicKeyBlob(publicKey);

            // create the StrongName
            return new StrongName(
                keyBlob, assemblyName.Name, assemblyName.Version);
        }

    }
}
